package com.tiancheng.trade.authserver.component.login;


import cn.dev33.satoken.stp.SaLoginModel;
import com.tiancheng.trade.authserver.constant.SystemConstant;
import com.tiancheng.trade.authserver.entity.AuthPermission;
import com.tiancheng.trade.authserver.entity.AuthRole;
import com.tiancheng.trade.authserver.entity.AuthUser;
import com.tiancheng.trade.authserver.entity.UserThirdInfo;
import com.tiancheng.trade.authserver.enums.ThirdPlatformEnums;
import com.tiancheng.trade.authserver.enums.UserGenderEnum;
import com.tiancheng.trade.commom.web.model.SubjectProfile;
import com.tiancheng.trade.authserver.service.IAuthPermissionService;
import com.tiancheng.trade.authserver.service.IAuthRoleService;
import com.tiancheng.trade.authserver.service.IAuthTokenService;
import com.tiancheng.trade.authserver.service.IUserThirdInfoService;
import com.tiancheng.trade.authserver.utils.IdGenerateUtils;
import com.tiancheng.trade.authserver.vo.UserLogInResultVO;
import com.tiancheng.trade.authserver.vo.UserLoginInVO;
import com.tiancheng.trade.commom.web.auth.StpCustomKit;
import com.tiancheng.trade.commom.web.config.PlatformRequestConfig;
import com.tiancheng.trade.commom.core.exception.error.AuthErrorInfoEnum;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public abstract class BaseUserLoginHandler implements UserLogInHandler {
    @Resource
    protected IAuthTokenService authTokenService;
    @Resource
    protected PlatformRequestConfig platformRequestProperties;
    @Resource
    protected IUserThirdInfoService userThirdInfoService;
    @Resource
    protected IAuthRoleService roleService;
    @Resource
    protected IAuthPermissionService permissionService;
    @Resource
    private HttpServletResponse response;

    /**
     * 参数校验
     */
    protected abstract void paramValidate(UserLoginInVO param, boolean checkCertificate);

    /**
     * 根据登录参数获取用户
     */
    protected abstract AuthUser getUserByParam(UserLoginInVO param, boolean checkCertificate);

    /**
     * 构建用户SubjectProfile
     */
    protected SubjectProfile buildSubjectProfile(AuthUser authUser) {
        SubjectProfile commonProfile = new SubjectProfile();
        commonProfile.setId(authUser.getId());
        commonProfile.getAttributes().put("name", authUser.getName());
        if (authUser.getIsTourist()) {
            commonProfile.getAttributes().put("isTourist", 1);
        }
        return commonProfile;
    }

    /**
     * 设置用户登陆三方信息
     */
    protected UserLogInResultVO setUserSignInThirdInfo(AuthUser authUser, UserLogInResultVO result) {
        result.setThirdPlatformBinds(new HashSet<>());
        if (!authUser.getIsTourist()) {
            UserThirdInfo tengyunInfo = this.userThirdInfoService.getByClientIdAndUserId(ThirdPlatformEnums.tiancheng,
                    this.platformRequestProperties.getClientId(), result.getId());
            if (tengyunInfo == null) {
                tengyunInfo = new UserThirdInfo();
                tengyunInfo.setPlatform(ThirdPlatformEnums.tiancheng.name());
                tengyunInfo.setClientId(this.platformRequestProperties.getClientId());
                tengyunInfo.setOpenId(IdGenerateUtils.generateOpenId(result.getId(),
                        this.platformRequestProperties.getClientId()));
                tengyunInfo.setNick(result.getName());
                tengyunInfo.setUserId(result.getId());
                tengyunInfo.setPhone(result.getPhone());
                tengyunInfo.setCreatedDt(LocalDateTime.now());
                this.userThirdInfoService.save(tengyunInfo);
            }
        }
        log.info("登录查询用户id:{}的所有三方信息", authUser.getId());
        List<UserThirdInfo> thirdInfos = this.userThirdInfoService.getByUserId(authUser.getId());
        if (thirdInfos != null) {
            thirdInfos.forEach(thirdInfo -> {
                if (thirdInfo.getPlatform() == ThirdPlatformEnums.tiancheng.name()) {
                    if (StringUtils.isNotEmpty(thirdInfo.getNick())) {
                        result.setNick(thirdInfo.getNick());
                    }
                    if (StringUtils.isNotEmpty(thirdInfo.getAvatar())) {
                        result.setAvatar(thirdInfo.getAvatar());
                    }
                    if (StringUtils.isNotEmpty(thirdInfo.getCity())) {
                        result.setCity(thirdInfo.getCity());
                    }
                    if (thirdInfo.getGender() != null && !thirdInfo.getGender().equals(UserGenderEnum.unknown.getCode())) {
                        final Optional<UserGenderEnum> genderEnum = UserGenderEnum.getByCode(thirdInfo.getGender());
                        result.setGender(genderEnum.get().getCode());
                        result.setGenderName(genderEnum.get().getDesc());
                    }
                    result.setOpenId(thirdInfo.getOpenId());
                }
                UserLogInResultVO.ThirdPlatformBind thirdPlatformBind = new UserLogInResultVO.ThirdPlatformBind()
                        .setPlatform(thirdInfo.getPlatform())
                        .setClientId(thirdInfo.getClientId())
                        .setNick(thirdInfo.getNick())
                        .setAvatar(thirdInfo.getAvatar())
                        .setOpenId(thirdInfo.getOpenId())
                        .setUnionId(thirdInfo.getUnionId());
                result.getThirdPlatformBinds().add(thirdPlatformBind);
            });
        }
        return result;
    }


    /**
     * 设置用户登陆角色权限信息
     */
    protected UserLogInResultVO setUserSignInRolePermission(UserLogInResultVO result, UserLoginInVO param) {
        List<AuthRole> roles = this.roleService.selectUserRolesByApplicationClientId(result.getId(), param.getClientId());
        if (roles != null) {
            result.setRoles(roles.stream().map(AuthRole::getRoleCode).collect(Collectors.toSet()));
        }
        List<AuthPermission> permissions = this.permissionService
                .getUserPermissionsByApplicationClientId(result.getId(), param.getClientId());
        if (permissions != null) {
            result.setPermissions(permissions.stream().map(AuthPermission::getCode).collect(Collectors.toSet()));
        }
        return result;
    }

    /**
     * 校验用户
     */
    protected void validateUserStatus(AuthUser authUser) {
        AuthErrorInfoEnum.USER_USER_IS_DISABLED.assertIsTrue(authUser.getStatus() == 0);
    }

    @Transactional
    public UserLogInResultVO _signIn(UserLoginInVO param, boolean checkCertificate) {
        //参数校验
        this.paramValidate(param, checkCertificate);
        //根据登陆参数获取用户
        AuthUser authUser = this.getUserByParam(param, checkCertificate);
        //校验用户状态
        this.validateUserStatus(authUser);
        SubjectProfile commonProfile = this.buildSubjectProfile(authUser);
        UserLogInResultVO result = new UserLogInResultVO();
        result.setId(StringUtils.isEmpty(authUser.getPhone()) && StringUtils.isEmpty(authUser.getEmail()) ? 0 : authUser.getId());
        result.setIsTourist(authUser.getIsTourist());
        result.setName(authUser.getName());
        result.setPhone(authUser.getPhone());
        if (result.getPhone() != null && result.getPhone().contains("@")) {
            result.setPhone(null);
        }
        result.setEmail(authUser.getEmail());
        result.setSignInType(param.getTokenType().name());
        result.setHasPassword(authUser.getPassword() != null);
        //设置用户登陆三方信息
        result = this.setUserSignInThirdInfo(authUser, result);
        Set<UserLogInResultVO.ThirdPlatformBind> thirdPlatformBinds = result.getThirdPlatformBinds();
        if (CollectionUtils.isNotEmpty(thirdPlatformBinds) && param.getThirdPlatform() != null) {
            for (UserLogInResultVO.ThirdPlatformBind thirdPlatformBind : thirdPlatformBinds) {
                if (thirdPlatformBind.getPlatform().equals(param.getThirdPlatform().name())) {
                    commonProfile.getAttributes().put("openId", thirdPlatformBind.getOpenId());
                    commonProfile.getAttributes().put("clientId", param.getClientId());
                }
            }
        }
        if (commonProfile.getAttribute("openId") == null) {
            commonProfile.getAttributes().put("openId", result.getOpenId());
            commonProfile.getAttributes().put("clientId", this.platformRequestProperties.getClientId());
        }
        String tokenStr = this.authTokenService.generate(param.getTokenType(), SystemConstant.userTokenTimeout,
                param.getThirdPlatform(), param.getClientId(), commonProfile, param.getThirdToken());
        result.setJwt(tokenStr);
        // SaLoginModel 配置登录相关参数
        StpCustomKit.DEFAULT.login(authUser.getId(), new SaLoginModel()
                .setDevice("PC")                // 此次登录的客户端设备类型, 用于[同端互斥登录]时指定此次登录的设备类型
                .setIsLastingCookie(true)        // 是否为持久Cookie（临时Cookie在浏览器关闭时会自动删除，持久Cookie在重新打开后依然存在）
                .setToken(tokenStr) // 预定此次登录生成的Token
                .setExtra("auth", result)    // Token挂载的扩展参数 （此方法只有在集成jwt插件时才会生效）
                .setIsWriteHeader(false)         // 是否在登录后将 Token 写入到响应头
        );

        StpCustomKit.DEFAULT.setTokenValue(tokenStr);
        //设置用户角色权限
        result = this.setUserSignInRolePermission(result, param);
        return result;
    }

    protected UserLogInResultVO lockSignIn(UserLoginInVO param, boolean checkCertificate) {
        return _signIn(param, checkCertificate);
    }

    @Override
    public UserLogInResultVO signIn(UserLoginInVO param, boolean checkCertificate) {
        int expireTime = (int) SystemConstant.userTokenTimeout - 300;
        UserLogInResultVO signInResult = this.lockSignIn(param, checkCertificate);
        List<UserLogInResultVO.AppCookie> cookies = new ArrayList<>(3);
        cookies.add(new UserLogInResultVO.AppCookie().setKey("w_uid").setValue(signInResult.getId().toString())
                .setExpiry(expireTime)
                .setDomain(this.platformRequestProperties.getRootDomain()));
        cookies.add(new UserLogInResultVO.AppCookie().setKey("w_open_id").setValue(signInResult.getOpenId())
                .setExpiry(expireTime)
                .setDomain(this.platformRequestProperties.getRootDomain()));
        cookies.add(new UserLogInResultVO.AppCookie().setKey("w_skey").setValue(signInResult.getJwt())
                .setExpiry(expireTime)
                .setDomain(this.platformRequestProperties.getRootDomain()));
        cookies.add(new UserLogInResultVO.AppCookie().setKey("jwt").setValue(signInResult.getJwt())
                .setExpiry(expireTime)
                .setDomain(this.platformRequestProperties.getRootDomain()));
        cookies.add(new UserLogInResultVO.AppCookie().setKey("w_expires_in").setValue(expireTime + "")
                .setExpiry(expireTime)
                .setDomain(this.platformRequestProperties.getRootDomain()));
        signInResult.setCookie(cookies);
        for (UserLogInResultVO.AppCookie appCookie : cookies) {
            Cookie cookie = new Cookie(appCookie.getKey(), appCookie.getValue());
            cookie.setMaxAge(appCookie.getExpiry());
            cookie.setDomain(appCookie.getDomain());
            cookie.setPath("/");
            this.response.addCookie(cookie);
        }
        return signInResult;
    }
}
